<?php

declare(strict_types=1);

use think\response\Json;

// 应用公共文件

/** 
 * ============================
 * -------- 数组处理类 --------
 * ============================
 */

if (!function_exists('arrayIsPureNumber')) {
	/**
	 * 数组是否是纯数字组成
	 * @param array $array
	 */
	function arrayIsPureNumber($array): bool
	{
		if (!is_array($array)) return false;
		foreach ($array as $k => $v) {
			if (!is_numeric($k)) return false;
		}
		return true;
	}
}

if (!function_exists('arrayDeleteSort')) {
	/**
	 * 删除数组中某个元素，并重新排列（整理下标）
	 * @param array $array
	 * @param mixed $index 要删除元素的下标（若传入null，则忽略下标，采用第三个参数方式）
	 * @param array $value 要删除元素的值（若第二个参数传入null，则本参数起作用）
	 */
	function arrayDeleteSort($array, $index = 0, $value = null): array
	{
		if (is_null($index)) {
			$result = array_filter($array, fn($it) => $it !== $value);
		} else {
			$result = $array;
			unset($result[$index]);
		}
		return array_merge($result);
	}
}

if (!function_exists('arrayFind')) {
	/**
	 * 在数组中查找
	 * 类似js的Array.find()
	 * @param mixed $array
	 * @param \Closure $callback
	 */
	function arrayFind($array, \Closure $callback)
	{
		foreach ($array as $v) {
			if ($callback($v)) return $v;
		}
		return null;
	}
}

/** 
 * ============================
 * -------- RSA加解密 --------
 * ============================
 */

if (!function_exists('rsaGenereateKey')) {
	/**
	 * 生成rsa密钥对
	 * @param integer $key_size 密钥尺寸
	 * @param string $digest 摘要算法，一般情况不要改
	 * @return array public, private
	 */
	function rsaGenereateKey(int $key_size = 1024, $digest = 'sha512'): array
	{
		// 生成密钥对
		$config = array(
			'digest_alg' => $digest,
			'private_key_bits' => $key_size,
			'private_key_type' => OPENSSL_KEYTYPE_RSA,
		);
		$res = openssl_pkey_new($config);
		// 提取私钥
		openssl_pkey_export($res, $private_key);
		// 提取公钥
		$public_key_details = openssl_pkey_get_details($res);
		$public_key = $public_key_details['key'];
		// 返回密钥对
		return ['private' => $private_key, 'public' => $public_key];
	}
}

if (!function_exists('rsaEncode')) {
	/**
	 * RSA分段加密
	 * @param string $public_key 公钥内容
	 * @param string $data 要加密的数据
	 * @throws Exception
	 */
	function rsaEncode(string $public_key, string $data): string
	{
		$crypto = '';
		// $padding是根据密钥的长度key_size自动计算的，一般1024对应128，2048对应256
		$key_size = openssl_pkey_get_details(openssl_pkey_get_public($public_key))['bits'] / 8;
		foreach (str_split($data, $key_size - 11) as $chunk) {
			$encryptData = '';
			if (openssl_public_encrypt($chunk, $encryptData, $public_key, OPENSSL_PKCS1_PADDING)) {
				$crypto .= $encryptData;
			} else {
				throw new \Exception('RSA加密失败');
			}
		}
		return base64_encode($crypto);
	}
}

if (!function_exists('rsaDecode')) {
	/**
	 * RSA分段解密
	 * @param string $private_key 私钥内容
	 * @param string $data 密文
	 * @throws Exception
	 */
	function rsaDecode(string $private_key, string $data): string
	{
		$crypto = '';
		// $padding是根据密钥的长度key_size自动计算的，一般1024对应128，2048对应256
		$key_size = openssl_pkey_get_details(openssl_pkey_get_private($private_key))['bits'] / 8;
		foreach (str_split(base64_decode($data), $key_size) as $chunk) {
			$decryptData = '';
			if (openssl_private_decrypt($chunk, $decryptData, $private_key, OPENSSL_PKCS1_PADDING)) {
				$crypto .= $decryptData;
			} else {
				throw new \Exception('RSA解密失败');
			}
		}
		return $crypto;
	}
}


/** 
 * =================================
 * -------- 字符/日期处理类 --------
 * =================================
 */

if (!function_exists('strCutBetween')) {
	/**
	 * 字符串截取指定两个字符之间的内容
	 * @param string $begin 开始标记
	 * @param string $end 结束标记
	 * @param string $str 原字符串
	 */
	function strCutBetween($begin, $end, $str): string
	{
		$b = mb_strpos($str, $begin) + mb_strlen($begin);
		$e = mb_strpos($str, $end) - $b;
		return mb_substr($str, $b, $e);
	}
}

if (!function_exists('hanGetFirstLetter')) {
	/**
	 * 获取汉字拼音首字母
	 * @param string $s0 汉字字符串
	 */
	function hanGetFirstLetter($s0): string|null
	{
		$fchar = ord(substr($s0, 0, 1));
		if (($fchar >= ord("a") and $fchar <= ord("z")) or ($fchar >= ord("A") and $fchar <= ord("Z"))) return strtoupper(chr($fchar));
		$s = iconv("UTF-8", "GBK", $s0);
		$asc = ord($s[0]) * 256 + ord($s[1]) - 65536;
		if ($asc >= -20319 and $asc <= -20284) return "A";
		if ($asc >= -20283 and $asc <= -19776) return "B";
		if ($asc >= -19775 and $asc <= -19219) return "C";
		if ($asc >= -19218 and $asc <= -18711) return "D";
		if ($asc >= -18710 and $asc <= -18527) return "E";
		if ($asc >= -18526 and $asc <= -18240) return "F";
		if ($asc >= -18239 and $asc <= -17923) return "G";
		if ($asc >= -17922 and $asc <= -17418) return "H";
		if ($asc >= -17417 and $asc <= -16475) return "J";
		if ($asc >= -16474 and $asc <= -16213) return "K";
		if ($asc >= -16212 and $asc <= -15641) return "L";
		if ($asc >= -15640 and $asc <= -15166) return "M";
		if ($asc >= -15165 and $asc <= -14923) return "N";
		if ($asc >= -14922 and $asc <= -14915) return "O";
		if ($asc >= -14914 and $asc <= -14631) return "P";
		if ($asc >= -14630 and $asc <= -14150) return "Q";
		if ($asc >= -14149 and $asc <= -14091) return "R";
		if ($asc >= -14090 and $asc <= -13319) return "S";
		if ($asc >= -13318 and $asc <= -12839) return "T";
		if ($asc >= -12838 and $asc <= -12557) return "W";
		if ($asc >= -12556 and $asc <= -11848) return "X";
		if ($asc >= -11847 and $asc <= -11056) return "Y";
		if ($asc >= -11055 and $asc <= -10247) return "Z";
		return null;
	}
}

if (!function_exists('numberToChinese')) {
	/**
	 * 数字转成中文
	 * @param number $number
	 */
	function numberToChinese($number)
	{
		$chineseNumber = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
		$unit = ['', '十', '百', '千', '万', '亿'];

		$numStr = strval($number);
		$length = strlen($numStr);
		$result = '';

		for ($i = 0; $i < $length; $i++) {
			$digit = (int)$numStr[$i];
			// 处理零的情况
			if ($digit === 0) {
				// 如果不是最后一个数字，并且下一个数字不是零，则添加“零”
				if ($i !== $length - 1 && (int)$numStr[$i + 1] !== 0) {
					$result .= '零';
				}
			} else {
				// 当前数字不是零，添加数字和单位
				$result .= $chineseNumber[$digit] . $unit[$length - $i - 1];
			}
		}

		return $result;
	}
}

if (!function_exists('weekToChinese')) {
	/**
	 * 获取星期的中文
	 * @param int $weekNumber
	 * @param string $prefix 前缀，默认'周'
	 */
	function weekToChinese($weekNumber, $prefix = '周')
	{
		$list = ['一', '二', '三', '四', '五', '六', '日'];
		$result = $list[intval($weekNumber) - 1] ?? null;
		return $result ? $prefix . $result : null;
	}
}

if (!function_exists('createRandomString')) {
	/**
	 * 创建随机字符串
	 * @param int $length 长度
	 */
	function createRandomString($length = 6, $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
	{
		$result = '';
		$maxIndex = strlen($characters) - 1;
		for ($i = 0; $i < $length; $i++) {
			$randomIndex = mt_rand(0, $maxIndex);
			$result .= $characters[$randomIndex];
		}
		return $result;
	}
}

/** 
 * ============================
 * -------- HTTP请求类 --------
 * ============================
 */

if (!function_exists('success')) {
	/**
	 * 响应成功消息
	 */
	function success($data = null, string $msg = 'ok', int $code = 0): Json
	{
		return json(compact('msg', 'code', 'data'));
	}
}

if (!function_exists('failed')) {
	/**
	 * 响应失败消息
	 */
	function failed(string $msg = 'failed', int $code = 1, $data = null): Json
	{
		return json(compact('msg', 'code', 'data'));
	}
}

if (!function_exists('imMsg')) {
	/**
	 * 给gateway worker发送约定格式的消息
	 */
	function imMsg(string $act = 'failed', $data = null): string
	{
		return json_encode(['act' => $act, 'data' => $data], JSON_UNESCAPED_UNICODE);
	}
}

if (!function_exists('curlGet')) {
	/**
	 * curl发起Get请求
	 * @param string $url
	 * @param array $query 数据（自动拼接）
	 * @param array $headers 请求头
	 * @param array $curlOptions 其他要合并给curl_setopt的选项，例如 [CURLOPT_TIMEOUT => 10]
	 * @param mixed &$refCh 可以返回curl句柄
	 * @throws \Exception
	 */
	function curlGet($url = '', $query = [], $headers = [], $curlOptions = [], &$refCh = null)
	{
		// 初始化 cURL 会话
		$ch = curl_init();

		// 拼接查询参数到 URL
		if (!empty($query)) {
			$url = rtrim($url, '?&') . '?' . http_build_query($query);
		}

		// 设置 cURL 选项
		curl_setopt($ch, CURLOPT_URL, $url); // 设置请求 URL
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应结果，而不是直接输出
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); // 设置请求头

		// 如果是 HTTPS 请求，则关闭 SSL 验证
		if (strpos($url, 'https://') === 0) {
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭对等证书验证
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 关闭主机名验证
		}

		// 设置其他 cURL 选项
		foreach ($curlOptions as $option => $value) {
			curl_setopt($ch, $option, $value);
		}

		$refCh = $ch;

		// 执行 cURL 请求并获取响应
		$response = curl_exec($ch);

		// 关闭
		curl_close($ch);

		// 检查是否有错误发生
		if ($response === false) {
			$error = curl_error($ch);
			$returnCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
			throw new \Exception($error, $returnCode);
		}

		// 处理响应体
		$responseContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

		// 根据 Content-Type 处理响应体
		if (strpos($responseContentType, 'application/json') !== false) {
			$decodedResponse = json_decode($response, true); // 将 JSON 响应解码为数组形式
			if ($decodedResponse !== null) {
				$response = $decodedResponse;
			}
		}

		return $response;
	}
}

if (!function_exists('curlPost')) {
	/**
	 * 发起post请求
	 * @param string $url 网址，如需传参数可以使用 $url . '?' . http_build_query($query) 的方式
	 * @param string|array $body 请求参数，如果是string或二进制则作为请求体直接传输，如果是数组则根据Content-Type自动处理
	 * @param array $headers 请求头（如果传入空数组，Content-Type默认设为 application/x-www-form-urlencoded），请注意请求头的设置方法是 ['Content-Type: application/json']
	 * @param array $curlOptions 其他要合并给curl_setopt的选项，例如 [CURLOPT_TIMEOUT => 10]
	 * @param mixed &$refCh 可以返回curl句柄
	 * @throws \Exception
	 */
	function curlPost(string $url, $body, $headers = [], $curlOptions = [], &$refCh = null)
	{
		// 初始化 cURL 会话
		$ch = curl_init();

		// 设置 cURL 选项
		curl_setopt($ch, CURLOPT_URL, $url); // 设置请求 URL
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回响应结果，而不是直接输出
		curl_setopt($ch, CURLOPT_POST, true); // 设置为 POST 请求

		// 如果是 HTTPS 请求，则关闭 SSL 验证
		if (strpos($url, 'https://') === 0) {
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 关闭对等证书验证
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 关闭主机名验证
		}

		// 设置请求头
		curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

		// 设置请求体
		if (is_array($body)) {
			// 根据 Content-Type 处理请求体
			$content_type = '';
			foreach ($headers as $header) {
				if (strpos($header, 'Content-Type:') === 0) {
					$content_type = trim(str_replace('Content-Type:', '', $header));
					break;
				}
			}

			if (strpos($content_type, 'application/json') !== false) {
				$body = json_encode($body, JSON_UNESCAPED_UNICODE); // 转换为 JSON 字符串
			} else if (strpos($content_type, 'multipart/form-data') !== false) {
				$body = http_build_query($body); // 转换为 URL 编码的字符串
			} else if (strpos($content_type, 'application/x-www-form-urlencoded') !== false) {
				$body = http_build_query($body); // 转换为 URL 编码的字符串
			} else {
				$body = http_build_query($body); // 默认转换为 URL 编码的字符串
			}
		}
		curl_setopt($ch, CURLOPT_POSTFIELDS, $body);

		// 设置其他 cURL 选项
		foreach ($curlOptions as $option => $value) {
			curl_setopt($ch, $option, $value);
		}

		$refCh = $ch;

		// 执行 cURL 请求并获取响应
		$response = curl_exec($ch);

		// 关闭
		curl_close($ch);

		// 检查是否有错误发生
		if ($response === false) {
			$error = curl_error($ch);
			$returnCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
			throw new \Exception($error, $returnCode);
		}

		// 处理响应体
		$responseContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

		// 根据 Content-Type 处理响应体
		if (strpos($responseContentType, 'application/json') !== false) {
			$decodedResponse = json_decode($response, true); // 将 JSON 响应解码为数组形式
			if ($decodedResponse !== null) {
				$response = $decodedResponse;
			}
		}

		return $response;
	}
}

/** 
 * ============================
 * ---------- 反射类 ----------
 * ============================
 */

if (!function_exists('parseReflectionClasses')) {
	/**
	 * 解析某个目录中的类
	 * @param string $dir 目录名称
	 * @param string $namespace 对应的命名空间
	 * @param boolean $deep 是否递归扫描目录深层
	 * @return array
	 */
	function parseReflectionNamespaceClasses($dir, $namespace, $deep = false)
	{
		$classes = [];

		$dir = rtrim($dir, '/') . '/';
		$namespace = rtrim($namespace, '\\') . '\\';

		// 如果目录不存在，则直接返回空数组
		if (!is_dir($dir)) return $classes;

		// 扫描目录，获取文件列表
		$files = scandir($dir);
		foreach ($files as $file) {
			if ($file === '.' || $file === '..') continue;

			$path = $dir . DIRECTORY_SEPARATOR . $file;

			// 如果是目录且允许递归扫描，则递归调用解析函数
			if (is_dir($path) && $deep) {
				$subNamespace = $namespace . $file;
				$classes = array_merge($classes, parseReflectionNamespaceClasses($path, $subNamespace, $deep));
			} elseif (is_file($path) && pathinfo($path, PATHINFO_EXTENSION) === 'php') {
				// 如果是 PHP 文件，则尝试加载并解析类
				$class = $namespace . pathinfo($file, PATHINFO_FILENAME);
				if (class_exists($class)) {
					$reflectionClass = new \ReflectionClass($class);
					$classes[] = $reflectionClass;
				}
			}
		}

		return $classes;
	}
}

if (!function_exists('parseReflectionComment')) {
	/**
	 * 解析反射的注解
	 * 把一个@开头的注释行按空格分割成数组
	 * @param string $comment
	 */
	function parseReflectionComment($comment)
	{
		if (!is_string($comment)) return [];
		$line_array = preg_split('/\r\n|\r|\n/', $comment);
		$result = [];
		foreach ($line_array as $line) {
			// 过滤无@开头的项
			if (strpos($line, '@') === false) continue;
			// 移除@之前的符号 [/ * @title 标题] ===> [title 标题]
			$res = preg_replace("/.+@/", '', $line);
			// 除去多个空格的情况 [param int id  商品ID] ===> [param int id 商品ID]
			$res = preg_replace("/\s+/", ' ', $res);
			// 按空格分隔 ['param', 'int', 'id', '商品ID'], 
			$result[] = explode(' ', $res);
		}
		return $result;
	}
}
